package cn.usbtg.session.impl;

import cn.usbtg.core.exception.BTGRuntimeException;
import cn.usbtg.session.BTGSession;
import cn.usbtg.session.BTGSessionDao;
import cn.usbtg.sutil.EmptyUtil;
import com.jfinal.plugin.activerecord.Record;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Hashtable;
import java.util.List;

/**
 * 基于db的session存储实现
 */
public class BTGDBSessionDao implements BTGSessionDao {
    protected BTGDBSessionDaoConfig config;

    public BTGDBSessionDao() {
        this.config = new BTGDBSessionDaoConfig();
    }

    public BTGDBSessionDao(String arpName) {
        this.config = new BTGDBSessionDaoConfig(arpName);
    }

    public BTGDBSessionDao(BTGDBSessionDaoConfig sessionDBConfig) {
        this.config = sessionDBConfig;
    }

    @Override
    public void saveSession(BTGSession session) {
        if (EmptyUtil.isEmpty(session)) {
            throw new BTGRuntimeException("session is null");
        }

        this.config.getDbPro().deleteById(this.config.getTableName(), this.config.getSessionIdColumnName(), session.getId());

        Record sessionRecord = new Record();
        sessionRecord.set(this.config.getSessionIdColumnName(), session.getId());
        sessionRecord.set(this.config.getSessionObjColumnName(), session);
        sessionRecord.set(this.config.getSessionLastActiveTimeColumnName(), session.getLastAccessedTime());
        sessionRecord.set(this.config.getSessionMaxInactiveIntervalColumnName(), session.getMaxInactiveInterval());
        this.config.getDbPro().save(this.config.getTableName(), sessionRecord);
    }

    @Override
    public void deleteSession(String sessionId) {
        if (EmptyUtil.isEmpty(sessionId)) {
            throw new BTGRuntimeException("session id is null");
        }
        this.config.getDbPro().deleteById(this.config.getTableName(), this.config.getSessionIdColumnName(), sessionId);
    }

    @Override
    public BTGSession getSession(String sessionId) {
        if (EmptyUtil.isEmpty(sessionId)) {
            throw new BTGRuntimeException("session id is null");
        }

        Record sessionRecord = this.config.getDbPro().findById(this.config.getTableName(), this.config.getSessionIdColumnName(), sessionId);
        if (EmptyUtil.isEmpty(sessionRecord)) {
            return null;
        }

        byte[] sessionBytes = sessionRecord.getBytes(this.config.getSessionObjColumnName());
        BTGSession session;
        try {
            ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(sessionBytes));
            session = (BTGSession) objectInputStream.readObject();
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException("get session error:" + e.getMessage(), e);
        }
        session.setLastAccessedTime(sessionRecord.getLong(this.config.getSessionLastActiveTimeColumnName()));

        return session;
    }

    @Override
    public void refreshSession(BTGSession session) {
        if (EmptyUtil.isEmpty(session)) {
            throw new BTGRuntimeException("session is null");
        }
        Record sessionRecord = new Record();
        sessionRecord.set(this.config.getSessionIdColumnName(), session.getId());
        sessionRecord.set(this.config.getSessionObjColumnName(), session);
        sessionRecord.set(this.config.getSessionLastActiveTimeColumnName(), session.getLastAccessedTime());
        sessionRecord.set(this.config.getSessionMaxInactiveIntervalColumnName(), session.getMaxInactiveInterval());
        this.config.getDbPro().update(this.config.getTableName(), this.config.getSessionIdColumnName(), sessionRecord);
    }

    @Override
    public void active(BTGSession session) {
        if (EmptyUtil.isEmpty(session)) {
            throw new BTGRuntimeException("session is null");
        }
        Record sessionRecord = new Record();
        sessionRecord.set(this.config.getSessionIdColumnName(), session.getId());
        sessionRecord.set(this.config.getSessionLastActiveTimeColumnName(), System.currentTimeMillis());
        sessionRecord.set(this.config.getSessionMaxInactiveIntervalColumnName(), session.getMaxInactiveInterval());
        this.config.getDbPro().update(this.config.getTableName(), this.config.getSessionIdColumnName(), sessionRecord);
    }

    @Override
    public Hashtable<String, BTGSession> getSessions() {
        Hashtable<String, BTGSession> sessions = new Hashtable<>();
        List<Record> records = this.config.getDbPro().find("select * from " + this.config.getTableName());
        for (Record record : records) {
            byte[] sessionBytes = record.getBytes(this.config.getSessionObjColumnName());
            BTGSession session;
            try {
                ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(sessionBytes));
                session = (BTGSession) objectInputStream.readObject();
                sessions.put(session.getId(), session);
            } catch (IOException | ClassNotFoundException e) {
                throw new RuntimeException("get session error:" + e.getMessage(), e);
            }
        }
        return sessions;
    }

    @Override
    public void clearTimeout() {
        //for (BTGSession session : this.getSessions().values()) {
        //    if (session.isInvalidate()) {
        //        this.deleteSession(session.getId());
        //    }
        //}
        this.config.getDbPro().update("delete from " + this.config.getTableName() + " where " + this.config.getSessionLastActiveTimeColumnName() + "  + " + this.config.getSessionMaxInactiveIntervalColumnName() + " < " + System.currentTimeMillis());
    }

    @Override
    public void clear() {
        this.config.getDbPro().update("truncate table " + this.config.getTableName());
    }
}